<!--
* @author wn
* @date 2022/05/26 10:37:40
* @description: 城市选择器 组件
!-->
<template>
	<div class="xtx-city" ref="target">
		<!-- 展示区 -->
		<div class="select" @click="toggle" :class="{ active: isShow }">
			<span class="placeholder" v-if="!fullLocation">{{ placeholder }}</span>
			<span class="value" v-else>{{ fullLocation }}</span>
			<i class="iconfont icon-angle-down"></i>
		</div>
		<!-- 选择区 -->
		<div class="option" v-show="isShow">
			<div class="loading" v-if="isLoading"></div>
			<template v-else>
				<span
					class="ellipsis"
					v-for="city in cityList"
					:key="city.id"
					@click="getAddress(city)"
				>
					{{ city.name }}
				</span>
			</template>
		</div>
	</div>
</template>
<script>
import { computed, ref, reactive } from 'vue'
import { onClickOutside } from '@vueuse/core'
import axios from 'axios'
export default {
	name: 'XtxCity',
	props: {
		fullLocation: {
			type: String,
			default: '',
		},
		placeholder: {
			type: String,
			default: '请选择配送地址',
		},
	},
	setup(props, { emit }) {
		const target = ref(null)
		const isLoading = ref(false)
		const isShow = ref(false)
		const cityData = ref(null)

		const address = reactive({
			provinceCode: '', // 所在省份编码
			provinceName: '', // 所在省份名字
			cityCode: '', // 所在城市编码
			cityName: '',
			countyCode: '', // 所在区/县编码
			countyName: '',
			fullLocation: '', // 完整地址，包括省市区3级的文字
		})

		/**
		 * 获取城市数据，考虑到全国所有城市数据量比较大，所以我们做缓存
		 * 因为函数内部有两种情况：
		 * 1 网络请求的
		 * 2 直接读取缓存(window对象上)
		 */
		const getCityData = async () => {
			if (!window.cityData) {
				const { data } = await axios(
					'https://yjy-oss-files.oss-cn-zhangjiakou.aliyuncs.com/tuxian/area.json'
				)
				// console.log(data)
				data && (window.cityData = data)
			}
			return window.cityData
		}

		const show = async () => {
			isShow.value = true
			isLoading.value = true
			cityData.value = await getCityData()
			isLoading.value = false
			// 重新打开城市选择器 数据清空  默认 省开始
			for (const key in address) {
				address[key] = ''
			}
		}
		const close = () => {
			// 表单验证 需要支持双向数据绑定
			if (!isShow.value) return
			isShow.value = false
			// 已经选好  再次点击打开  啥也不干 在关闭  不执行发送数据
			if (props.fullLocation) return
			emit('change', undefined)
		}
		// 修改地址时 组件初始化的时候  需要 模拟 input v-model 向父组件发送数据
		// 变相 向 Schema 赋值
		props.fullLocation && emit('change', props.fullLocation)
		const toggle = () => {
			isShow.value ? close() : show()
		}
		// 是否点击盒子外面区域
		// 1 目标元素 2 点击该元素外的区域触发函数
		onClickOutside(target, () => close())

		// 计算一个用于给用户选择的列表 三级联动
		// 有值 说明被点击了  计算出下一级的数据
		const cityList = computed(() => {
			// 默认情况下，显示省  provinceCode = ''
			let temp = cityData.value
			// 根据省的编码获取到 市 的列表
			if (address.provinceCode) {
				temp = temp.find(item => item.code === address.provinceCode).areaList
			}

			// 根据市的编码获取到 区 的列表
			if (address.cityCode) {
				temp = temp.find(item => item.code === address.cityCode).areaList
			}
			return temp
		})
		// 点击 数据 第一次点了省
		const getAddress = city => {
			//   省
			if (city.level === 0) {
				address.provinceCode = city.code
				address.provinceName = city.name
			}
			//   市
			if (city.level === 1) {
				address.cityCode = city.code
				address.cityName = city.name
			}
			//   区
			if (city.level === 2) {
				address.countyCode = city.code
				address.countyName = city.name
				// 点击完 最后 区 已经获取完数据
				address.fullLocation = `${address.provinceName} ${address.cityName} ${address.countyName}`
				// 最后一级，需要隐藏面板
				close()
				// 触发父组件的自定义事件，允许父组件获取当前子组件选中的地址
				emit('change', address)
			}
		}
		return { target, toggle, isShow, isLoading, cityList, getAddress }
	},
}
</script>
<style scoped lang="less">
.xtx-city {
	display: inline-block;
	position: relative;
	// 与放大镜冲突
	z-index: 400;
	// display: inline-block;
	.select {
		height: 30px;
		border: 1px solid #e4e4e4;
		line-height: 28px;
		padding: 0 5px;
		cursor: pointer;
		&.active {
			background: #fff;
		}
		.placeholder {
			color: #999;
		}
		.value {
			color: #666;
			font-size: 12px;
		}
		i {
			font-size: 12px;
			margin-left: 5px;
		}
	}
	.option {
		display: flex;
		flex-wrap: wrap;
		position: absolute;
		left: 0;
		top: 29px;
		width: 542px;
		border: 1px solid #e4e4e4;
		background: #fff;
		min-height: 30px;
		line-height: 30px;
		padding: 10px;
		& > span {
			width: 130px;
			text-align: center;
			cursor: pointer;
			border-radius: 4px;
			padding: 0 3px;
			&:hover {
				background: #f5f5f5;
			}
		}
		.loading {
			height: 290px;
			width: 100%;
			background: url(~@/assets/images/loading.gif) no-repeat center;
		}
	}
}
</style>
